<template>
   <div>
       <div id="container" ref="container"></div>
   </div>
</template>

<script lang='ts'>
   import { computed, getCurrentInstance, ref, watch,onMounted,defineComponent, toRaw } from 'vue';
   import { templateRef } from '@vueuse/core';
   import { GUI } from '../../../public/dat';
   import { TeapotGeometry } from '../../../public/teapot.js';
   let $THREE = window.THREE;
   export default defineComponent({
       props:{},
       setup(props,context){
            let {ctx}:any = getCurrentInstance();
            const container = templateRef<any>('container');
            const camera = ref<any>(null);
            const cameraControls = ref<any>(null);
            const effectController = ref<any>(null);
            const teapotSize = 400;
            const ambientLight = ref<any>(null);
            const light = ref<any>(null);
            const tess = ref<number>(-1);
            const bBottom = ref<any>(null);
            const bLid = ref<any>(null);
            const bBody = ref<any>(null);
            const bFitLid = ref<any>(null);
            const bNonBlinn= ref<any>(null);
            const shading = ref<any>(null);
            const wireMaterial = ref<any>(null);
            const flatMaterial = ref<any>(null);
            const gouraudMaterial = ref<any>(null);
            const phongMaterial = ref<any>(null);
            const texturedMaterial = ref<any>(null);
            const reflectiveMaterial = ref<any>(null);
            const teapot = ref<any>(null);
            const textureCube = ref<any>(null);
            const diffuseColor = new $THREE.Color();
			const specularColor = new $THREE.Color();
            const renderer = ref<any>(null);
            const mesh = ref<any>(null);
            const scene = ref<any>(new $THREE.Scene());
            
            // RENDERER
			// renderer.value = new $THREE.WebGLRenderer( { antialias: true } );
			// renderer.value.setPixelRatio( window.devicePixelRatio );
			// renderer.value.setSize( canvasWidth, canvasHeight );
			// renderer.value.outputEncoding = $THREE.sRGBEncoding;
			// container.value.appendChild( renderer.domElement );

            //模拟初始化结束
            const initThree =()=>{
                //场景
                const canvasWidth = window.innerWidth;
				const canvasHeight = window.innerHeight;
                // CAMERA
				camera.value = new $THREE.PerspectiveCamera( 45, canvasWidth / canvasHeight, 1, 80000 );
				camera.value.position.set( - 600, 550, 1300 );
				// LIGHTS
				ambientLight.value = new $THREE.AmbientLight( 0x333333 );	// 0.2

				light.value = new $THREE.DirectionalLight( 0xFFFFFF, 1.0 );
				// direction is set in GUI

				// RENDERER
				renderer.value = new $THREE.WebGLRenderer( { antialias: true } );
				renderer.value.setPixelRatio(window.devicePixelRatio );
				renderer.value.setSize( canvasWidth, canvasHeight );
				renderer.value.outputEncoding = $THREE.sRGBEncoding;
				container.value.appendChild( renderer.value.domElement );

				// EVENTS
				window.addEventListener( 'resize', onWindowResize );

				// CONTROLS
                let controls = new $THREE.OrbitControls(camera.value, renderer.value.domElement);
				cameraControls.value = new $THREE.OrbitControls( camera.value, renderer.value.domElement );
				cameraControls.value.addEventListener('change', render);

				// TEXTURE MAP
				const textureMap = new $THREE.TextureLoader().load( 'textures/cube/uv_grid_opengl.jpg' );
				textureMap.wrapS = textureMap.wrapT = $THREE.RepeatWrapping;
				textureMap.anisotropy = 16;
				textureMap.encoding = $THREE.sRGBEncoding;

				// REFLECTION MAP
				const path = "textures/cube/";
				const urls = [
					path + "px.png", path + "nx.png",
					path + "py.png", path + "ny.png",
					path + "pz.png", path + "nz.png"
				];

				textureCube.value = new $THREE.CubeTextureLoader().load(urls);
				textureCube.value.encoding = $THREE.sRGBEncoding;

				// MATERIALS
				const materialColor = new $THREE.Color();
				materialColor.setRGB( 1.0, 1.0, 1.0 );

				wireMaterial.value = new $THREE.MeshBasicMaterial( { color: 0xFFFFFF, wireframe: true } );

				flatMaterial.value = new $THREE.MeshPhongMaterial( { color: materialColor, specular: 0x000000, flatShading: true, side: $THREE.DoubleSide } );

				gouraudMaterial.value = new $THREE.MeshLambertMaterial( { color: materialColor, side: $THREE.DoubleSide } );

				phongMaterial.value = new $THREE.MeshPhongMaterial( { color: materialColor, side: $THREE.DoubleSide } );

				texturedMaterial.value = new $THREE.MeshPhongMaterial( { color: materialColor, map: textureMap, side: $THREE.DoubleSide } );

				reflectiveMaterial.value = new $THREE.MeshPhongMaterial( { color: materialColor, envMap: textureCube, side: $THREE.DoubleSide } );

				scene.value.background = new $THREE.Color( 0xAAAAAA );

				scene.value.add(toRaw(ambientLight.value));
                scene.value.add(toRaw(light.value));
                setupGui();

            }
            
            const setupGui =()=> {

				effectController.value = {
					shininess: 40.0,
					ka: 0.17,
					kd: 0.51,
					ks: 0.2,
					metallic: true,
					hue:	0.121,
					saturation: 0.73,
					lightness: 0.66,
					lhue:	0.04,
					lsaturation: 0.01,	// non-zero so that fractions will be shown
					llightness: 1.0,
					// bizarrely, if you initialize these with negative numbers, the sliders
					// will not show any decimal places.
					lx: 0.32,
					ly: 0.39,
					lz: 0.7,
					newTess: 15,
					bottom: true,
					lid: true,
					body: true,
					fitLid: false,
					nonblinn: false,
					newShading: "flat"
				};

				let h;

				let gui:any = new GUI();

				// material (attributes)

				h = gui.addFolder( "Material control" );

				h.add( effectController.value, "shininess", 1.0, 400.0, 1.0 ).name( "shininess" ).onChange(render);
				h.add( effectController.value, "kd", 0.0, 1.0, 0.025 ).name( "diffuse strength" ).onChange(render);
				h.add( effectController.value, "ks", 0.0, 1.0, 0.025 ).name( "specular strength" ).onChange(render);
				h.add( effectController.value, "metallic" ).onChange(render);

				// material (color)

				h = gui.addFolder( "Material color" );

				h.add( effectController.value, "hue", 0.0, 1.0, 0.025 ).name( "hue" ).onChange(render);
				h.add( effectController.value, "saturation", 0.0, 1.0, 0.025 ).name( "saturation" ).onChange( render );
				h.add( effectController.value, "lightness", 0.0, 1.0, 0.025 ).name( "lightness" ).onChange( render );

				// light (point)

				h = gui.addFolder( "Lighting" );

				h.add( effectController.value, "lhue", 0.0, 1.0, 0.025 ).name( "hue" ).onChange( render );
				h.add( effectController.value, "lsaturation", 0.0, 1.0, 0.025 ).name( "saturation" ).onChange( render );
				h.add( effectController.value, "llightness", 0.0, 1.0, 0.025 ).name( "lightness" ).onChange( render );
				h.add( effectController.value, "ka", 0.0, 1.0, 0.025 ).name( "ambient" ).onChange( render );

				// light (directional)

				h = gui.addFolder( "Light direction" );

				h.add( effectController.value, "lx", - 1.0, 1.0, 0.025 ).name( "x" ).onChange( render );
				h.add( effectController.value, "ly", - 1.0, 1.0, 0.025 ).name( "y" ).onChange( render );
				h.add( effectController.value, "lz", - 1.0, 1.0, 0.025 ).name( "z" ).onChange( render );

				h = gui.addFolder( "Tessellation control" );
				h.add( effectController.value, "newTess", [ 2, 3, 4, 5, 6, 8, 10, 15, 20, 30, 40, 50 ] ).name( "Tessellation Level" ).onChange( render );
				h.add( effectController.value, "lid" ).name( "display lid" ).onChange( render );
				h.add( effectController.value, "body" ).name( "display body" ).onChange( render );
				h.add( effectController.value, "bottom" ).name( "display bottom" ).onChange( render );
				h.add( effectController.value, "fitLid" ).name( "snug lid" ).onChange( render );
				h.add( effectController.value, "nonblinn" ).name( "original scale" ).onChange( render );

				// shading

				gui.add( effectController.value, "newShading", [ "wireframe", "flat", "smooth", "glossy", "textured", "reflective" ] ).name( "Shading" ).onChange( render );
                

			}


            const render =()=> {

				if ( effectController.value.newTess !== tess.value ||
					effectController.value.bottom !== bBottom.value ||
					effectController.value.lid !== bLid.value ||
					effectController.value.body !== bBody.value ||
					effectController.value.fitLid !== bFitLid.value ||
					effectController.value.nonblinn !== bNonBlinn.value ||
					effectController.value.newShading !== shading.value ) {

					tess.value = effectController.value.newTess;
					bBottom.value = effectController.value.bottom;
					bLid.value = effectController.value.lid;
					bBody.value = effectController.value.body;
					bFitLid.value = effectController.value.fitLid;
					bNonBlinn.value = effectController.value.nonblinn;
					shading.value = effectController.value.newShading;

					createNewTeapot();

				}

				// We're a bit lazy here. We could check to see if any material attributes changed and update
				// only if they have. But, these calls are cheap enough and this is just a demo.
				phongMaterial.value.shininess = effectController.value.shininess;
				texturedMaterial.value.shininess = effectController.value.shininess;

				diffuseColor.setHSL( effectController.value.hue, effectController.value.saturation, effectController.value.lightness );
				if ( effectController.value.metallic ) {

					// make colors match to give a more metallic look
					specularColor.copy( diffuseColor );

				} else {

					// more of a plastic look
					specularColor.setRGB( 1, 1, 1 );

				}

				diffuseColor.multiplyScalar(effectController.value.kd);
				flatMaterial.value.color.copy(diffuseColor);
				gouraudMaterial.value.color.copy(diffuseColor);
				phongMaterial.value.color.copy(diffuseColor);
				texturedMaterial.value.color.copy(diffuseColor);

				specularColor.multiplyScalar(effectController.ks);
				phongMaterial.value.specular.copy(specularColor);
				texturedMaterial.value.specular.copy(specularColor);

				// Ambient's actually controlled by the light for this demo
				ambientLight.value.color.setHSL( effectController.value.hue, effectController.value.saturation, effectController.value.lightness * effectController.value.ka );

				light.value.position.set( effectController.value.lx, effectController.value.ly, effectController.value.lz );
				light.value.color.setHSL( effectController.value.lhue, effectController.value.lsaturation, effectController.value.llightness );

				// skybox is rendered separately, so that it is always behind the teapot.
				if ( shading === "reflective" ) {

					scene.value.background = textureCube;

				} else {

					scene.value.background = null;

				}

				renderer.value.render(toRaw(scene.value), toRaw(camera.value));

			}
			//
            const createNewTeapot =()=> {

				if (teapot.value !== undefined && teapot.value!=null) {
                    console.log(teapot);
					teapot.value.geometry.dispose();
					scene.value.remove(toRaw(teapot.value));

				}

				const teapotGeometry = new TeapotGeometry( teapotSize,
					tess.value,
					effectController.value.bottom,
					effectController.value.lid,
					effectController.value.body,
					effectController.value.fitLid,
					! effectController.value.nonblinn );
				teapot.value = new $THREE.Mesh(
					teapotGeometry,
					shading.value === "wireframe" ? wireMaterial.value : (
						shading.value === "flat" ? flatMaterial.value : (
							shading.value === "smooth" ? gouraudMaterial.value : (
								shading.value === "glossy" ? phongMaterial.value : (
									shading.value === "textured" ? texturedMaterial.value : reflectiveMaterial.value ) ) ) ) );	// if no match, pick Phong
                console.log(teapot);
				scene.value.add(teapot.value);

			}

            // 添加window 的resize事件监听
            window.addEventListener('resize',onWindowResize);

            function onWindowResize() {
				const canvasWidth = window.innerWidth;
				const canvasHeight = window.innerHeight;
				renderer.value.setSize( canvasWidth, canvasHeight );
				camera.value.aspect = canvasWidth / canvasHeight;
				camera.value.updateProjectionMatrix();
				render();

			}

            onMounted(()=>{
                initThree();
                render();
            })

           return{
               camera,
               renderer
           }
       }
   })
</script>

<style scoped>
</style>
